package org.ghost.wechat.platform.common.redis;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.ghost.wechat.platform.config.properties.RedisProperties;
import org.ghost.wechat.platform.util.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Tuple;
import redis.clients.util.Pool;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 通过自定义json序列化
 *
 * @author 01
 */
@Component
public class RedisCacheUtil implements InitializingBean {
    private Logger logger = LoggerFactory.getLogger(RedisCacheUtil.class);

    private final static String LOCKED = "1";
    private RedisJackson2JsonRedisSerializer redisSerializer = new RedisJackson2JsonRedisSerializer();

    @Autowired
    private Pool<Jedis> jedisPool;

    @Autowired
    private RedisProperties redisProperties;

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.jedisPool, "jedisPool not null");
    }

    public <T> void set(String key, T obj) {
        if (StringUtils.isNotBlank(key) && obj != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.set(this.keyBytes(key), this.valueBytes(obj));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.set报错:" + e.getMessage(), e);
            }
        }
    }

    public <T> void set(String key, T obj, long timeout) {
        if (StringUtils.isNotBlank(key) && obj != null && timeout > 0) {
            this.set(key, obj, (int) timeout);
        }
    }

    public <T> void set(String key, T obj, int timeout) {
        if (StringUtils.isNotBlank(key) && obj != null && timeout > 0) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.setex(this.keyBytes(key), timeout, this.valueBytes(obj));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.set报错:" + e.getMessage(), e);
            }
        }
    }

    public <T> void set(final String key, final T value, final long timeout, final TimeUnit unit) {
        if (StringUtils.isNotBlank(key) && value != null && timeout > 0 && unit != null) {
            this.set(key, value, (int) TimeUnit.SECONDS.convert(timeout, unit));
        }
    }

    public Long del(String key) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.del(this.keyBytes(key));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.del报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public Long expire(String key, int seconds) {
        if (StringUtils.isNotBlank(key) && seconds > 0) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.expire(this.keyBytes(key), seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.expire报错:" + e.getMessage(), e);
            }
        }

        return 0L;
    }

    public Long expireAt(String key, long unixTime) {
        if (StringUtils.isNotBlank(key) && unixTime > 0) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.expireAt(this.keyBytes(key), unixTime);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.expireAt报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public String mset(byte[]... keysValues) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.mset(keysValues);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.mSet报错:" + e.getMessage(), e);
        }

        return null;
    }

    public String mset(String... keysValues) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.mset(keysValues);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.mSet报错:" + e.getMessage(), e);
        }

        return null;
    }

    public Long msetnx(String... keysValues) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.msetnx(keysValues);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.mSet报错:" + e.getMessage(), e);
        }

        return 0L;
    }

    public Long msetnx(byte[]... keysValues) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.msetnx(keysValues);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.mSet报错:" + e.getMessage(), e);
        }

        return 0L;
    }

    public List<String> mget(String... keys) {
        if (ArrayUtils.isNotEmpty(keys)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                byte[][] bkeys = new byte[keys.length][];
                for (int i = 0; i < bkeys.length; ++i) {
                    bkeys[i] = this.keyBytes(keys[i]);
                }
                List<byte[]> result = jedis.mget(bkeys);
                if (CollectionUtils.isNotEmpty(result)) {
                    return result.stream().map(it -> {
                        if (ArrayUtils.isNotEmpty(it)) {
                            try {
                                return new String(it, "UTF-8");
                            } catch (UnsupportedEncodingException e) {
                                logger.error("*****RedisCacheUtil.mGet报错:" + e.getMessage(), e);
                                return null;
                            }
                        } else {
                            return null;
                        }
                    }).collect(Collectors.toList());
                }
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.mGet报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public Long incr(String key) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.incr(this.keyBytes(key));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.incr报错:" + e.getMessage(), e);
            }
        }

        return 0L;
    }

    public Long incrBy(String key, long integer) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.incrBy(this.keyBytes(key), integer);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.incrBy报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public Double incrByFloat(String key, double value) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.incrByFloat(this.keyBytes(key), value);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.incrByFloat报错:" + e.getMessage(), e);
            }
        }
        return 0.0;
    }

    public String getSet(String key, String value) {
        if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.getSet(this.keyBytes(key), this.valueBytes(value)), String.class);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.getSet报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public Set<String> keys(String pattern) {
        if (StringUtils.isNotBlank(pattern)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.keys(pattern);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.keys报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public boolean exists(String key) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.exists(this.keyBytes(key));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.exists报错:" + e.getMessage(), e);
            }
        }

        return false;
    }

    public <T> List<T> lrange(String key, long beginIndex, long endIndex, Class<T> clz) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                List<byte[]> returnList = jedis.lrange(this.keyBytes(key), beginIndex, endIndex);
                if (CollectionUtils.isNotEmpty(returnList)) {
                    return returnList.stream().map(it -> {
                        if (ArrayUtils.isNotEmpty(it)) {
                            return this.redisSerializer.deserialize(it, clz);
                        } else {
                            return null;
                        }
                    }).collect(Collectors.toList());
                }
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lrange报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public Long lpush(String key, Object obj) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.lpush(this.keyBytes(key), this.valueBytes(obj));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lpush报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public void lpush(String key, Object obj, int seconds) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.lpush(this.keyBytes(key), this.valueBytes(obj));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lpush报错:" + e.getMessage(), e);
            }
        }
    }

    public <T> Long lpush(String key, Collection<T> values) {
        if (StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.lpush(this.keyBytes(key), values.stream().map(this::valueBytes).toArray(byte[][]::new));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lpush报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public <T> void lpush(String key, Collection<T> values, int seconds) {
        if (StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.lpush(this.keyBytes(key), values.stream().map(this::valueBytes).toArray(byte[][]::new));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lpush报错:" + e.getMessage(), e);
            }
        }
    }

    public Long lpushx(String key, Object obj) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.lpushx(this.keyBytes(key), this.valueBytes(obj));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lpushx报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public void lpushx(String key, Object obj, int seconds) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.lpushx(this.keyBytes(key), this.valueBytes(obj));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lpushx报错:" + e.getMessage(), e);
            }
        }
    }

    public <T> Long lpushx(String key, Collection<T> values) {
        if (StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.lpushx(this.keyBytes(key), values.stream().map(this::valueBytes).toArray(byte[][]::new));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lpushx报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public <T> void lpushx(String key, Collection<T> values, int seconds) {
        if (StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.lpushx(this.keyBytes(key), values.stream().map(this::valueBytes).toArray(byte[][]::new));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.lpushx报错:" + e.getMessage(), e);
            }
        }
    }

    public Long rpush(String key, Object obj) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.rpush(this.keyBytes(key), this.valueBytes(obj));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.rpush报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public void rpush(String key, Object obj, int seconds) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.rpush(this.keyBytes(key), this.valueBytes(obj));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.rpush报错:" + e.getMessage(), e);
            }
        }
    }

    public <T> Long rpush(String key, Collection<T> values) {
        if (StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.rpush(this.keyBytes(key), values.stream().map(this::valueBytes).toArray(byte[][]::new));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.rightPushAll报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public <T> void rpush(String key, Collection<T> values, int seconds) {
        if (StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.rpush(this.keyBytes(key), values.stream().map(this::valueBytes).toArray(byte[][]::new));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.rightPushAll报错:" + e.getMessage(), e);
            }
        }
    }

    public Long rpushx(String key, Object obj) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.rpushx(this.keyBytes(key), this.valueBytes(obj));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.rpushx报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public void rpushx(String key, Object obj, int seconds) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.rpushx(this.keyBytes(key), this.valueBytes(obj));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.rpushx报错:" + e.getMessage(), e);
            }
        }
    }

    public <T> Long rpushx(String key, Collection<T> values) {
        if (StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.rpushx(this.keyBytes(key), values.stream().map(this::valueBytes).toArray(byte[][]::new));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.rightPushAll报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public <T> void rpushx(String key, Collection<T> values, int seconds) {
        if (StringUtils.isNotBlank(key) && CollectionUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.rpushx(this.keyBytes(key), values.stream().map(this::valueBytes).toArray(byte[][]::new));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.rightPushAll报错:" + e.getMessage(), e);
            }
        }
    }

    public String lset(String key, long index, Object value) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.lset(this.keyBytes(key), index, this.valueBytes(value));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.setListValue报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public void lset(String key, long index, Object value, int seconds) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.lset(this.keyBytes(key), index, this.valueBytes(value));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.setListValue报错:" + e.getMessage(), e);
            }
        }
    }


    public <T> T lpop(String key, Class<T> clz) {
        if (StringUtils.isNotBlank(key) && clz != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.lpop(this.keyBytes(key)), clz);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.leftPop报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public <T> T rpop(String key, Class<T> clz) {
        if (StringUtils.isNotBlank(key) && clz != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.rpop(this.keyBytes(key)), clz);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.leftPop报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public Long llen(String key) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.llen(key);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.llen报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public Long hdel(final String key, final String... fields) {
        if (StringUtils.isNotBlank(key) && ArrayUtils.isNotEmpty(fields)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                if (ArrayUtils.isNotEmpty(fields)) {
                    byte[][] bField = new byte[fields.length][];
                    for (int i = 0; i < bField.length; ++i) {
                        bField[i] = this.keyBytes(fields[i]);
                    }
                    return jedis.hdel(this.keyBytes(key), bField);
                } else {
                    return jedis.hdel(this.keyBytes(key));
                }
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hdel报错:" + e.getMessage(), e);
            }
        }

        return 0L;
    }

    public <T> void hset(String key, String field, T value, long timeout, TimeUnit timeUnit) {
        if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(field) && value != null && timeUnit != null) {
            this.hset(key, field, value, (int) TimeUnit.SECONDS.convert(timeout, timeUnit));
        }
    }

    public <T> void hset(String key, String field, T value, Integer seconds) {
        if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(field) && value != null && seconds != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.hset(this.keyBytes(key), this.keyBytes(field), this.valueBytes(value));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hset报错:" + e.getMessage(), e);
            }
        }
    }

    /**
     * 最好指定具体类型
     *
     * @param key
     * @param hashKey
     * @param <T>
     * @return
     */
    @Deprecated
    public <T> T hget(String key, String hashKey) {
        if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(hashKey)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return (T) this.redisSerializer.deserialize(jedis.hget(this.keyBytes(key), this.keyBytes(hashKey)));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hget报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public <T> T hget(String key, String hashKey, final Class<T> clz) {
        if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(hashKey) && clz != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.hget(this.keyBytes(key), this.keyBytes(hashKey)), clz);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hget报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    /**
     * 最好指定具体类型
     *
     * @param key
     * @param <T>
     * @return
     */
    @Deprecated
    public <T> Map<String, T> hgetAllToMap(String key) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                Map<byte[], byte[]> entries = jedis.hgetAll(this.keyBytes(key));
                if (MapUtils.isNotEmpty(entries)) {
                    Map<String, T> resultMap = new HashMap<String, T>();
                    for (Map.Entry<byte[], byte[]> entry : entries.entrySet()) {
                        resultMap.put(this.keyString(entry.getKey()), (T) this.redisSerializer.deserialize(entry.getValue()));
                    }
                    return resultMap;
                }
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hgetAllToMap报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public <T> Map<String, T> hgetAllToMap(String key, final Class<T> clz) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                Map<byte[], byte[]> entries = jedis.hgetAll(this.keyBytes(key));
                if (MapUtils.isNotEmpty(entries)) {
                    Map<String, T> resultMap = new HashMap<String, T>();
                    for (Map.Entry<byte[], byte[]> entry : entries.entrySet()) {
                        resultMap.put(this.keyString(entry.getKey()), this.redisSerializer.deserialize(entry.getValue(), clz));
                    }
                    return resultMap;
                }
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hgetAllToMap报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    /**
     * 最好指定具体类型
     *
     * @param key
     * @param <T>
     * @return
     */
    @Deprecated
    public <T> List<T> hgetAllToList(String key) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                Map<byte[], byte[]> entries = jedis.hgetAll(this.keyBytes(key));
                if (MapUtils.isNotEmpty(entries)) {
                    List<T> resultList = new ArrayList<T>();
                    for (Map.Entry<byte[], byte[]> entry : entries.entrySet()) {
                        resultList.add((T) this.redisSerializer.deserialize(entry.getValue()));
                    }
                    return resultList;
                }
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hgetAllToList报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public <T> List<T> hgetAllToList(String key, final Class<T> clz) {
        if (StringUtils.isNotBlank(key) && clz != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                Map<byte[], byte[]> entries = jedis.hgetAll(this.keyBytes(key));
                if (MapUtils.isNotEmpty(entries)) {
                    List<T> resultList = new ArrayList<T>();
                    for (Map.Entry<byte[], byte[]> entry : entries.entrySet()) {
                        resultList.add(this.redisSerializer.deserialize(entry.getValue(), clz));
                    }
                    return resultList;
                }
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hgetAllToList报错:" + e.getMessage(), e);
            }
        }
        return null;
    }

    public void hmset(String key, Map<String, String> map, int seconds) {
        if (StringUtils.isNotBlank(key) && MapUtils.isNotEmpty(map)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                Map<byte[], byte[]> hashes = new HashMap<byte[], byte[]>();
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    hashes.put(this.keyBytes(entry.getKey()), this.valueBytes(entry.getValue()));
                }
                jedis.hmset(this.keyBytes(key), hashes);
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hsetAll报错:" + e.getMessage(), e);
            }
        }
    }

    public Long hlen(String key) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return jedis.hlen(this.keyBytes(key));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.hLen报错:" + e.getMessage(), e);
            }
        }
        return 0L;
    }

    public <T> void sadd(String key, int seconds, T... values) {
        if (StringUtils.isNotBlank(key) && ArrayUtils.isNotEmpty(values)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.sadd(this.keyBytes(key), this.rawValues(values));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.sadd报错:" + e.getMessage(), e);
            }
        }
    }

    public void zadd(String key, double score, Object member, int seconds) {
        if (StringUtils.isNotBlank(key) && member != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.zadd(this.keyBytes(key), score, this.valueBytes(member));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.zadd报错:" + e.getMessage(), e);
            }

        }
    }

    public void zaddStr(String key, double score, String member, int seconds) {
        if (StringUtils.isNotBlank(key) && member != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                jedis.zadd(this.keyBytes(key), score, this.valueBytes(member));
                this.expire(key, seconds);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.zaddStr报错:" + e.getMessage(), e);
            }

        }
    }

    public Set<String> zrangeStr(String key, long start, long stop) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                Set<byte[]> returnSet = jedis.zrange(this.keyBytes(key), start, stop);
                Set<String> resultSet = new HashSet<String>();
                for (byte[] bytes : returnSet) {
                    resultSet.add((String) this.redisSerializer.deserialize(bytes));
                }
                return resultSet;
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.zrangeStr报错:" + e.getMessage(), e);
            }
        }
        return null;
    }


    /**
     * 建议使用具体类型,否则类型转换可能会报错
     *
     * @param key
     * @param <T>
     * @return
     */
    @Deprecated
    public <T> T get(String key) {
        if (StringUtils.isNotBlank(key)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return (T) this.redisSerializer.deserialize(jedis.get(this.keyBytes(key)));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.get报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public <T> T get(String key, JavaType type) {
        if (StringUtils.isNotBlank(key) && type != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return redisSerializer.deserialize(jedis.get(this.keyBytes(key)), type);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.get报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public <T> T get(final String key, final Class<T> clz) {
        if (StringUtils.isNotBlank(key) && clz != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.get(this.keyBytes(key)), clz);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.get报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public <T> T get(final String key, final ParameterizedType parameterizedType) {
        if (StringUtils.isNotBlank(key) && parameterizedType != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.get(this.keyBytes(key)), parameterizedType);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.get报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public <T> T get(final String key, final TypeReference<T> typeReference) {
        if (StringUtils.isNotBlank(key) && typeReference != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.get(this.keyBytes(key)), typeReference);
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.get报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public <T> T get(final String key, final Class rawType, final Type ownerType, final Type... argType) {
        if (StringUtils.isNotBlank(key) && rawType != null && ArrayUtils.isNotEmpty(argType)) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.get(this.keyBytes(key)), ParameterizedTypeImpl.make(rawType, argType, ownerType));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.get报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public <T> List<T> getList(final String key, final Class<T> clz) {
        if (StringUtils.isNotBlank(key) && clz != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.get(this.keyBytes(key)), ParameterizedTypeImpl.make(List.class, new Type[]{clz}, null));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.get报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public <T> List<T> getList(final String key, final TypeReference<T> typeReference) {
        if (StringUtils.isNotBlank(key) && typeReference != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return this.redisSerializer.deserialize(jedis.get(this.keyBytes(key)), ParameterizedTypeImpl.make(List.class, new Type[]{typeReference.getType()}, null));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.get报错:" + e.getMessage(), e);
            }
        }

        return null;
    }

    public <T> Boolean setIfAbsent(final String key, final T value) {
        if (key != null) {
            String newValue = this.getString(value);
            Jedis jedis = null;
            try {
                jedis = this.jedisPool.getResource();
                return Objects.equals(1L, jedis.setnx(key, newValue));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.set报错:" + e.getMessage(), e);
            } finally {
                if (jedis != null) {
                    jedis.close();
                }
            }
        }

        return false;
    }

    public Boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return Objects.equals(1L, this.expire(key, (int) TimeUnit.SECONDS.convert(timeout, unit)));
    }

    public Boolean expireAt(final String key, final Date date) {
        if (StringUtils.isNotBlank(key) && date != null) {
            try (Jedis jedis = this.jedisPool.getResource()) {
                return Objects.equals(1L, jedis.expireAt(this.keyBytes(key), date.getTime() / 1000));
            } catch (Exception e) {
                logger.error("*****RedisCacheUtil.expireAt报错:" + e.getMessage(), e);
            }
        }

        return false;
    }


    public <T> Boolean zsetAdd(final String key, final T value, final double score) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            Long num = jedis.zadd(this.keyBytes(key), score, this.valueBytes(value));
            return num != null && num > 0L;
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetAdd报错:" + e.getMessage(), e);
        }

        return false;
    }

    public Long zsetRemoveRange(final String key, final long start, final long end) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.zremrangeByRank(key, start, end);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRemoveRange报错:" + e.getMessage(), e);
        }

        return 0L;
    }

    public Long zsetRemove(final String key, String... values) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.zrem(key, values);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRemove报错:" + e.getMessage(), e);
        }

        return 0L;
    }

    public Long zsetRemoveRangeByScore(final String key, final double min, final double max) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.zremrangeByScore(key, min, max);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRemoveRangeByScore报错:" + e.getMessage(), e);
        }

        return 0L;
    }


    public Long zsetCard(final String key) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.zcard(key);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetCard报错:" + e.getMessage(), e);
        }

        return 0L;
    }


    public <T> Set<T> setRange(final String key, final long start, final long end, final Class<T> clz) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            Set<String> set = jedis.zrange(key, start, end);
            if (set != null) {
                return set.stream().filter(Objects::nonNull).map(it -> getValue(it, clz)).filter(Objects::nonNull).collect(Collectors.toSet());
            }
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRangeByScore报错:" + e.getMessage(), e);
        }

        return null;
    }

    public Set<ZSetOperations.TypedTuple<String>> zsetRangeByScoreWithScores(final String key, final double min, final double max) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            Set<Tuple> set = jedis.zrangeByScoreWithScores(key, min, max);
            if (set != null) {
                return set.stream().filter(Objects::nonNull).map(it -> new DefaultTypedTuple<String>(it.getElement(), it.getScore())).collect(Collectors.toSet());
            }
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRangeByScoreWithScores报错:" + e.getMessage(), e);
        }

        return null;
    }


    public <T> Set<ZSetOperations.TypedTuple<T>> zsetRangeByScoreWithScores(final String key, final double min, final double max, final Class<T> clz) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            Set<Tuple> set = jedis.zrangeByScoreWithScores(key, min, max);
            if (set != null) {
                return set.stream().filter(Objects::nonNull).map(it -> new DefaultTypedTuple<T>(getValue(it.getElement(), clz), it.getScore())).collect(Collectors.toSet());
            }
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRangeByScoreWithScores报错:" + e.getMessage(), e);
        }

        return null;
    }

    public Long zsetRank(final String key, final Object o) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.zrank(this.keyBytes(key), this.valueBytes(o));
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRank报错:" + e.getMessage(), e);
        }

        return 0L;
    }

    public Long zsetCount(final String key, final double min, final double max) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            return jedis.zcount(key, min, max);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetCount报错:" + e.getMessage(), e);
        }

        return 0L;
    }

    public <T> Set<T> zsetRangeByScore(final String key, final double min, final double max, final Class<T> clz) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            Set<String> set = jedis.zrangeByScore(key, min, max);
            if (set != null) {
                return set.stream().filter(Objects::nonNull).map(it -> getValue(it, clz)).filter(Objects::nonNull).collect(Collectors.toSet());
            }
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRangeByScore报错:" + e.getMessage(), e);
        }

        return null;
    }

    public <T> Set<T> zsetRangeByScore(final String key, final double min, final double max, final int offset, final int count, final Class<T> clz) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            Set<String> set = jedis.zrangeByScore(key, min, max, offset, count);
            if (set != null) {
                return set.stream().filter(Objects::nonNull).map(it -> getValue(it, clz)).filter(Objects::nonNull).collect(Collectors.toSet());
            }
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.zsetRangeByScore报错:" + e.getMessage(), e);
        }

        return null;
    }

    /**
     * 尝试一次获取锁
     *
     * @param key     缓存key
     * @param timeout 设置缓存超时时间
     * @param unit    设置缓存超时单位
     * @return
     */
    public boolean tryLock(final String key, final long timeout, final TimeUnit unit) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            if (jedis == null) {
                logger.warn("*****RedisCacheUtil.tryLock获取redis连接失败");
                return false;
            }
            if (1L == jedis.setnx(key, LOCKED)) {
                jedis.expire(key, (int) TimeUnit.SECONDS.convert(timeout, unit));
                return true;
            }
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.tryLock出现错误:{},{}", e.getMessage(), e);
        }

        return false;
    }

    /**
     * 获取锁
     *
     * @param key          缓存key
     * @param retryTimeout 等待毫秒
     * @param timeout      设置缓存超时时间
     * @param unit         设置缓存超时单位
     * @return
     */
    public boolean lock(final String key, final long retryTimeout, final long timeout, final TimeUnit unit) {
        long beginTime = System.currentTimeMillis();
        try (Jedis jedis = this.jedisPool.getResource()) {
            if (jedis == null) {
                logger.warn("*****RedisCacheUtil.lock获取redis连接失败");
                return false;
            }
            while (System.currentTimeMillis() - beginTime < retryTimeout) {
                if (1L == jedis.setnx(key, LOCKED)) {
                    jedis.expire(key, (int) TimeUnit.SECONDS.convert(timeout, unit));
                    return true;
                }

                //等待一会儿
                sleep(100);
            }
        } catch (Exception e) {
            unlock(key);
            logger.error("*****RedisCacheUtil.lock出现错误:{},{}", e.getMessage(), e);
        }

        return false;
    }

    /**
     * 获取锁
     *
     * @param key          缓存key
     * @param retryTimeout 等待毫秒
     * @param date         设置缓存超时时间点
     * @return
     */
    public boolean lock(final String key, final long retryTimeout, Date date) {
        long beginTime = System.currentTimeMillis();
        try (Jedis jedis = this.jedisPool.getResource()) {
            if (jedis == null) {
                logger.warn("*****RedisCacheUtil.lock获取redis连接失败");
                return false;
            }
            while (System.currentTimeMillis() - beginTime < retryTimeout) {
                if (1L == jedis.setnx(key, LOCKED)) {
                    jedis.expire(key, (int) (date.getTime() - System.currentTimeMillis()));
                    return true;
                }

                //等待一会儿
                sleep(100);
            }
        } catch (Exception e) {
            unlock(key);
            logger.error("*****RedisCacheUtil.lock出现错误:{},{}", e.getMessage(), e);
        }

        return false;
    }

    /**
     * 删除key
     *
     * @param key 缓存key
     */
    public void unlock(String key) {
        try (Jedis jedis = this.jedisPool.getResource()) {
            if (jedis == null) {
                logger.warn("*****RedisCacheUtil.unlock获取redis连接失败");
            }
            jedis.del(key);
        } catch (Exception e) {
            logger.error("*****RedisCacheUtil.unlock出现错误:{},{}", e.getMessage(), e);
        }
    }

    /**
     * 休眠
     *
     * @param millis 毫秒
     */
    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            logger.error("*****RedisUtil.sleep出现错误:{},{}", e.getMessage(), e);
        }
    }


    private <T> boolean hasBaseType(T value) {
        if (value instanceof Byte || value instanceof Character || value instanceof Boolean
                || value instanceof Short || value instanceof Integer || value instanceof Long
                || value instanceof Float || value instanceof Double || value instanceof String) {
            return true;
        }

        return false;
    }

    private <T> boolean hasBaseType(Class<T> clz) {
        if (clz == Byte.class || clz == Character.class || clz == Boolean.class
                || clz == Short.class || clz == Integer.class || clz == Long.class
                || clz == Float.class || clz == Double.class || clz == String.class) {
            return true;
        }

        return false;
    }

    private <T> T getValue(final String val, final Class<T> clz) {
        if (StringUtils.isNotBlank(val)) {
            if (hasBaseType(clz)) {
                return getBaseTypeValue(val, clz);
            } else {
                return JacksonUtil.useDefaultMapper().fromJson(val, clz);
            }
        }
        return null;
    }

    private <T> T getValue(final String val, final ParameterizedType parameterizedType) {
        if (StringUtils.isNotBlank(val)) {
            TypeReference<T> typeReference = new TypeReference<T>() {
                @Override
                public Type getType() {
                    return parameterizedType;
                }
            };
            return JacksonUtil.useDefaultMapper().fromJson(val, typeReference);
        }

        return null;
    }

    private <T> T getValue(final String val, final TypeReference<T> typeReference) {
        if (StringUtils.isNotBlank(val)) {
            return JacksonUtil.useDefaultMapper().fromJson(val, typeReference);
        }

        return null;
    }

    private <T> T getValue(final String val, final Class rawType, final Type ownerType, final Type... argType) {
        if (StringUtils.isNotBlank(val)) {
            ParameterizedType parameterizedType = ParameterizedTypeImpl.make(rawType, argType, ownerType);
            TypeReference<T> typeReference = new TypeReference<T>() {
                @Override
                public Type getType() {
                    return parameterizedType;
                }
            };
            return JacksonUtil.useDefaultMapper().fromJson(val, typeReference);
        }

        return null;
    }

    /**
     * 几种基本类型处理
     *
     * @param value
     * @param clz
     * @param <T>
     * @return
     */
    @SuppressWarnings("unchecked")
    private <T> T getBaseTypeValue(String value, Class<T> clz) {
        if (StringUtils.isNotBlank(value)) {
            if (clz == Character.class) {
                char ch = value.toCharArray()[0];
                try {
                    Method method = clz.getMethod("valueOf", char.class);
                    if (method != null) {
                        return (T) method.invoke(clz.getClass(), ch);
                    }
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    logger.error("*****RedisCacheUtil.getBaseTypeValue出现错误:" + e.getMessage(), e);
                }
            } else if (clz == Byte.class || clz == Boolean.class
                    || clz == Short.class || clz == Integer.class || clz == Long.class
                    || clz == Float.class || clz == Double.class) {
                try {
                    Method method = clz.getMethod("valueOf", String.class);
                    if (method != null) {
                        return (T) method.invoke(clz.getClass(), value);
                    }
                } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                    logger.error("*****RedisCacheUtil.getBaseTypeValue出现错误:" + e.getMessage(), e);
                }
            } else if (clz == String.class) {
                return (T) value;
            }
        }

        return null;
    }

    public <T> String getString(T value) {
        if (value != null) {
            if (hasBaseType(value)) {
                return value.toString();
            } else {
                return JacksonUtil.nonDefaultMapper().toJson(value);
            }
        }

        return null;
    }

    protected byte[][] rawValues(Object... values) {
        if (ArrayUtils.isNotEmpty(values)) {
            return Stream.of(values).map(this::valueBytes).toArray(byte[][]::new);
        }
        return null;
    }

    protected byte[] keyBytes(String key) {
        byte[] keyByte;
        if (this.redisProperties.getKeySerializer()) {
            keyByte = this.valueBytes(key);
        } else {
            keyByte = key.getBytes();
        }

        return keyByte;
    }

    protected String keyString(byte[] key) {
        String keyString;
        if (this.redisProperties.getKeySerializer()) {
            keyString = (String) this.redisSerializer.deserialize(key);
        } else {
            keyString = new String(key);
        }

        return keyString;
    }

    @SuppressWarnings("unchecked")
    protected byte[] valueBytes(Object value) {
        return this.redisSerializer.serialize(value);
    }
}
